热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

C++|栈的应用(逆波兰算法)|计算器

后缀式的特点可用于计算表达式的值,我们通过中缀转后缀的方式可以将原表达式重新排列组合成为计算机易于理解的计算方式。详情请参考:逆波兰算法(

后缀式的特点可用于计算表达式的值,我们通过中缀转后缀的方式可以将原表达式重新排列组合成为计算机易于理解的计算方式。
详情请参考:逆波兰算法(后缀表达式)

关于中缀转后缀

中缀式转后缀式 如:
1+2就是一个中缀式,转换成后缀式为1 2 +
1+2*3…… ……1 2 3 * +
1+(2+5)…… ……1 2 5 + +

在计算时,从最左侧开始先选定一个符号该符号左边的两个数结合运算。以下(黄色高亮部分)计算过程如下

  • 1+2…… ……1 2 +

1与2进行+运算

  • 1+2*3…… ……1 2 3 * +

1. 最左边的符号 * ,左侧的两个数 2 3计算:2*3 = 6表达式:1 6 +
2. 最左边的符号 + ,左侧的两个数 1 6计算:1+6 = 7

  • 1+(2+5)…… ……1 2 5 + +

3. ...计算:2+5=7表达式:1 7 +
4. ...计算:1+7=8

如上所示,使用后缀式可以清晰的表示出表达式中的运算关系,所有运算符符号中,优先级最高的在最左边。

计算时从最左侧的符号先开始,且每个符号都与它左侧最近的两个数据结合

可以说,后缀式就是根据计算表达式中的优先级进行设计的。
比如,在计算表达式中 ’ ( ) ’ 的优先级最高,因此在转后缀式时遇到 ‘(’ 与 ‘)’ 之间的符号就认为是优先级最高的符号。

eg: a*(b+c)
a b c + *过程详解:
1、===a*(b+c)===↑
后缀: a ✔
符号:
2、===a*(b+c)===↑
后缀: a
符号: * ✔
3、===a*(b+c)===↑
后缀: a
符号: * ( ✔
4、===a*(b+c)===↑
后缀: a b ✔
符号: * (
5、===a*(b+c)===↑
后缀: a b
符号: * ( + ✔
6、===a*(b+c)===↑
后缀: a b c ✔
符号: * ( +
7、===a*(b+c)===↑
后缀: a b c
符号: * ( + ) ✔
注:遇到')',后面进来的符号都视为较低优先级符号,因此把' ( ) '内的符号全部转移到后缀式中
8、=============
后缀: a b c + ✔
符号: *
9、=============
后缀: a b c + * ✔
符号:

中缀转后缀技巧

中缀转后缀的核心就是,将优先级高的运算放在最前边计算,如 … (b+c)… 形式的表达式 ,要变成 … b c + … 。后缀式中优先级体现在符号自左向右的顺序,表达式的关系体现在每个符号与其左边最近的两个数字

而针对表达式 a+b-c ,因为 ‘+’ ‘-’ 优先级相同,而我们的计算顺序为同优先级自左向右,则我们认为左边的 ‘+’ 优先级更高。因此后缀式为 a b + c - ,其中 a b + 为(a+b) 就是原式中优先级高的部分。

而对于 a + b * c 的表达式,显然 b*c 的优先级最高,则后缀式为 a b c * + 。

在本例中,函数 void InfixToSuffix(); 进行中缀转后缀计算。

中缀转后缀计算机代码

#include using std::cin;
using std::cout;
using std::endl;template<typename T>
class Stack
{
public:Stack(int _m_size &#61; 20) :m_size(_m_size), mtop(0){data &#61; new T[m_size]();}~Stack(){delete[] data;}/* 判空 */bool empty(){return mtop &#61;&#61; 0;}void push(int val){if (Full()){throw std::exception("error: Stack is Full !");}data[mtop] &#61; val;mtop&#43;&#43;;}T top(){if (empty()){throw std::exception("error: The stack is empty !");}return data[mtop - 1];}void pop(){if (empty()){throw std::exception("error: Stack is Empty !");}mtop--;}void show(){if (empty()){cout << endl;}else{int i &#61; 0;while (i < mtop){cout << data[i] << " ";i&#43;&#43;;}cout << endl;}}
private:bool Full(){return mtop &#61;&#61; m_size;}T* data;int mtop;int m_size;
};/* 计算器类 */
class Calculator
{
public:// 这里m_size 默认给的是40&#xff0c;m_size限制表达式的长短Calculator(int _m_size &#61; 40) :m_size(_m_size), m_result(0){m_str &#61; new char[_m_size &#43; 1]();}~Calculator(){delete[] m_str;}/* 输入 */void setData(){cin.getline(m_str, m_size - 1); //最多输入m_size -1个&#xff0c;末尾加&#39;/0&#39;}/* 中缀转后缀 */void InfixToSuffix(); /* 把m_str输入的中缀式转换为后缀式 *//* 后缀计算 */void Compure();/* 计算结果 */double Getm_result(){return m_result;}
private:bool IsPop(char a, char b);char* m_str;double m_result;int m_size;};int main()
{while (1) {Calculator ca;ca.setData();ca.InfixToSuffix();ca.Compure();cout << ca.Getm_result() << endl;}return 0;
}// 比较符号优先级&#xff0c;是否出符号栈
// 栈顶元素b的优先级是否高于符号a&#xff0c; true/false 出栈b/入栈a
bool Calculator::IsPop(char a, char b) //a为待比较符号&#xff0c;b为符号栈栈顶元素
{if (b &#61;&#61; &#39;(&#39;) return false; //‘&#xff08;’优先级最低 if (a &#61;&#61; &#39;*&#39; || a &#61;&#61; &#39;/&#39;){if (b &#61;&#61; &#39;&#43;&#39; || b &#61;&#61; &#39;-&#39;){//可以入符号栈 // 栈外优先级高&#xff0c;可以继续入栈return false;}else{ return true;}}//if (a &#61;&#61; &#39;&#43;&#39; || a &#61;&#61; &#39;-&#39;)//{//a符号为最低优先级&#xff0c;符号栈出栈return true;//}
}/* 中缀转后缀 入栈 */
void Calculator::InfixToSuffix()
{// 转换为后缀式后&#xff0c;为了区分数字&#xff1a;如5、4……&#xff0c;在原来的数据增加了分隔符// dst 中存储输入的表达式的后缀式形式 size*2char* dst &#61; new char[m_size*2](); /* 符号栈 */Stack<char> symbol; // 在转后缀的过程中&#xff0c;&#xff08;通过符号优先级&#xff09;控制符号在后缀式中的位置int i &#61; 0;int k &#61; 0;while (m_str[i] !&#61; &#39;\0&#39;){/*----------- 特殊符号判断------------------------------*/if (m_str[i] &#61;&#61; &#39; &#39;) //如果当前字符是空格&#xff0c;则往后走一个{i&#43;&#43;;continue;}else if (m_str[i] &#61;&#61; &#39;(&#39;) //左括号直接入栈{symbol.push(m_str[i]);}else if (m_str[i] &#61;&#61; &#39;)&#39;) //遇到 &#xff09; &#xff0c;输出&#xff08; &#xff09;之间的所有符号{while (symbol.top() !&#61; &#39;(&#39;){dst[k&#43;&#43;] &#61; symbol.top();dst[k&#43;&#43;] &#61; &#39; &#39;;symbol.pop();}symbol.pop();}/*----------------- 数字 -------------------------------*/else if (isdigit(m_str[i])){dst[k&#43;&#43;] &#61; m_str[i];if (!isdigit(m_str[i &#43; 1])) //数字后加空格{dst[k&#43;&#43;] &#61; &#39; &#39;;}}/*----------------- 运算符 -------------------------------*/else{switch (m_str[i]){case &#39;&#43;&#39;:case &#39;-&#39;:case &#39;*&#39;:case &#39;/&#39;:if (symbol.empty()) //如果符号栈为空&#xff0c;直接入符号{symbol.push(m_str[i]);}else //否则&#xff0c;判断是否选择入符号栈还是出栈顶元素{// 栈内符号优先级高于栈外&#xff0c;出栈。 IsPop &#61;&#61; true;if (IsPop(m_str[i], symbol.top())) //出符号栈{dst[k&#43;&#43;] &#61; symbol.top();symbol.pop();continue;}else //当前符号优先级高&#xff0c;入符号栈{symbol.push(m_str[i]);}}break;default:break;}}i&#43;&#43;; /* 遍历下一字符 */}/*字符串已遍历完&#xff0c;把符号栈中剩余的符号入栈到数字栈中 */while (!symbol.empty()){dst[k&#43;&#43;] &#61; symbol.top();symbol.pop();}dst[k] &#61; &#39;\0&#39;;// 输出后缀式//cout <// 后缀表达式 保存到 Calculator::m_str中// 交换dst 与 m_str 的堆区空间char* tmp &#61; dst;dst &#61; m_str;m_str &#61; tmp; delete[]dst;
}void Calculator::Compure()
{/* 辅助栈 */Stack<double> st;int i &#61; 0;while (m_str[i] !&#61; &#39;\0&#39;){/*----------- 特殊符号判断------------------------------*/if (m_str[i] &#61;&#61; &#39; &#39;) //如果当前字符是空格&#xff0c;则往后走一个{i&#43;&#43;;continue;}/*----------------- 数字 -------------------------------*/else if (isdigit(m_str[i])){double tmp &#61; 0;while (m_str[i] !&#61; &#39; &#39;){tmp &#61; tmp * 10 &#43; m_str[i] - &#39;0&#39;;i&#43;&#43;;}st.push(tmp);}/*----------------- 运算符 -------------------------------*/else if (!st.empty()) //非空{double tmp &#61; 0;switch (m_str[i]){case &#39;&#43;&#39;:tmp &#43;&#61; st.top();st.pop();tmp &#43;&#61; st.top();st.pop();st.push(tmp);break;case &#39;-&#39;:tmp -&#61; st.top();st.pop();tmp &#43;&#61; st.top();st.pop();st.push(tmp);break;case &#39;*&#39;:tmp &#61; st.top();st.pop();tmp *&#61; st.top();st.pop();st.push(tmp);break;case &#39;/&#39;:{tmp &#61; st.top();st.pop();if (tmp !&#61; 0){tmp &#61; st.top() / tmp;st.pop();st.push(tmp);}else{throw std::exception("error: Divisor of 0 &#xff01;");}}break;default:break;}}i&#43;&#43;; /* 遍历下一字符 */}this->m_result &#61; st.top();st.top();
}

运行截图&#xff1a;
在这里插入图片描述

注&#xff1a;在计算器类中

Calculator(int _m_size &#61; 40) :m_size(_m_size), m_result(0)
{}

构造函数 _m_size 默认设置的为40&#xff0c;这将影响到我们输入的计算表达式的长度&#xff0c;我们在编译时可以根据需求自行更改代码。


推荐阅读
  • 本文讨论了使用差分约束系统求解House Man跳跃问题的思路与方法。给定一组不同高度,要求从最低点跳跃到最高点,每次跳跃的距离不超过D,并且不能改变给定的顺序。通过建立差分约束系统,将问题转化为图的建立和查询距离的问题。文章详细介绍了建立约束条件的方法,并使用SPFA算法判环并输出结果。同时还讨论了建边方向和跳跃顺序的关系。 ... [详细]
  • 3.223.28周学习总结中的贪心作业收获及困惑
    本文是对3.223.28周学习总结中的贪心作业进行总结,作者在解题过程中参考了他人的代码,但前提是要先理解题目并有解题思路。作者分享了自己在贪心作业中的收获,同时提到了一道让他困惑的题目,即input details部分引发的疑惑。 ... [详细]
  • STL迭代器的种类及其功能介绍
    本文介绍了标准模板库(STL)定义的五种迭代器的种类和功能。通过图表展示了这几种迭代器之间的关系,并详细描述了各个迭代器的功能和使用方法。其中,输入迭代器用于从容器中读取元素,输出迭代器用于向容器中写入元素,正向迭代器是输入迭代器和输出迭代器的组合。本文的目的是帮助读者更好地理解STL迭代器的使用方法和特点。 ... [详细]
  • 本文介绍了九度OnlineJudge中的1002题目“Grading”的解决方法。该题目要求设计一个公平的评分过程,将每个考题分配给3个独立的专家,如果他们的评分不一致,则需要请一位裁判做出最终决定。文章详细描述了评分规则,并给出了解决该问题的程序。 ... [详细]
  • 本文主要解析了Open judge C16H问题中涉及到的Magical Balls的快速幂和逆元算法,并给出了问题的解析和解决方法。详细介绍了问题的背景和规则,并给出了相应的算法解析和实现步骤。通过本文的解析,读者可以更好地理解和解决Open judge C16H问题中的Magical Balls部分。 ... [详细]
  • 本文介绍了C函数ispunct()的用法及示例代码。ispunct()函数用于检查传递的字符是否是标点符号,如果是标点符号则返回非零值,否则返回零。示例代码演示了如何使用ispunct()函数来判断字符是否为标点符号。 ... [详细]
  • 动态规划算法的基本步骤及最长递增子序列问题详解
    本文详细介绍了动态规划算法的基本步骤,包括划分阶段、选择状态、决策和状态转移方程,并以最长递增子序列问题为例进行了详细解析。动态规划算法的有效性依赖于问题本身所具有的最优子结构性质和子问题重叠性质。通过将子问题的解保存在一个表中,在以后尽可能多地利用这些子问题的解,从而提高算法的效率。 ... [详细]
  • Go Cobra命令行工具入门教程
    本文介绍了Go语言实现的命令行工具Cobra的基本概念、安装方法和入门实践。Cobra被广泛应用于各种项目中,如Kubernetes、Hugo和Github CLI等。通过使用Cobra,我们可以快速创建命令行工具,适用于写测试脚本和各种服务的Admin CLI。文章还通过一个简单的demo演示了Cobra的使用方法。 ... [详细]
  • 开发笔记:实验7的文件读写操作
    本文介绍了使用C++的ofstream和ifstream类进行文件读写操作的方法,包括创建文件、写入文件和读取文件的过程。同时还介绍了如何判断文件是否成功打开和关闭文件的方法。通过本文的学习,读者可以了解如何在C++中进行文件读写操作。 ... [详细]
  • 本文介绍了最长上升子序列问题的一个变种解法,通过记录拐点的位置,将问题拆分为左右两个LIS问题。详细讲解了算法的实现过程,并给出了相应的代码。 ... [详细]
  • 李逍遥寻找仙药的迷阵之旅
    本文讲述了少年李逍遥为了救治婶婶的病情,前往仙灵岛寻找仙药的故事。他需要穿越一个由M×N个方格组成的迷阵,有些方格内有怪物,有些方格是安全的。李逍遥需要避开有怪物的方格,并经过最少的方格,找到仙药。在寻找的过程中,他还会遇到神秘人物。本文提供了一个迷阵样例及李逍遥找到仙药的路线。 ... [详细]
  • Java SE从入门到放弃(三)的逻辑运算符详解
    本文详细介绍了Java SE中的逻辑运算符,包括逻辑运算符的操作和运算结果,以及与运算符的不同之处。通过代码演示,展示了逻辑运算符的使用方法和注意事项。文章以Java SE从入门到放弃(三)为背景,对逻辑运算符进行了深入的解析。 ... [详细]
  • 本文介绍了Codeforces Round #321 (Div. 2)比赛中的问题Kefa and Dishes,通过状压和spfa算法解决了这个问题。给定一个有向图,求在不超过m步的情况下,能获得的最大权值和。点不能重复走。文章详细介绍了问题的题意、解题思路和代码实现。 ... [详细]
  • 十大经典排序算法动图演示+Python实现
    本文介绍了十大经典排序算法的原理、演示和Python实现。排序算法分为内部排序和外部排序,常见的内部排序算法有插入排序、希尔排序、选择排序、冒泡排序、归并排序、快速排序、堆排序、基数排序等。文章还解释了时间复杂度和稳定性的概念,并提供了相关的名词解释。 ... [详细]
  • 本文介绍了PHP常量的定义和使用方法,包括常量的命名规则、大小写敏感性、全局范围和标量数据的限制。同时还提到了应尽量避免定义resource常量,并给出了使用define()函数定义常量的示例。 ... [详细]
author-avatar
cindy蔡79
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有